/***************************************************************************
 *   Copyright (C) 2009 by Bernhard Neuhofer   *
 *   mail@bneu.at   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

/*! \mainpage Warp4
 *
 * \section intro_sec Introduction
 *
 * OO-Framework for TW and SQTW Simulation in C++
 *
 */


#include <iostream>
#include <vector>
#include <typeinfo>
#include <boost/program_options.hpp>
#include <boost/thread.hpp>
#include "../tools/log.h"
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/xtime.hpp>
#include "../tools/mycorba.h"

using namespace std;

#include "message.h"
#include "taxtArrival.h"
#include "CustomerArrival.h"
#include "factories.h"
#include "TaxiStandMeFactory.h"
#include "sourceLp.h"
#include "modelentity.h"
#include "customersSouceMeFactory.h"
#include "LvtBasesSchedFactory.h"
#include "randomSchedFactory.h"
#include "roundRobinSchedFactory.h"
#include "submodelManager.h"
#include "sourceLPFactory.h"
#include "rollbackableLpFactory.h"
#include "taxiStandRollbackableAdapter.h"
#include "taxistandRollbackableAdapterFactory.h"
#include "taxistandSqtwAdapterFactory.h"
#include "corbaMessageHandler.h"
#include "CClientSM.h"
#include "smRun.h"
#include "smSetup.h"


namespace po = boost::program_options;

unsigned int debuglevel=0;
boost::shared_mutex  SMrunready;

char* _siorRun;



//Sets up the SMSetup Corba Interface for the AddressServer
void listenSMsetup()
{
    try {
        FILE_LOG ( logINFO ) <<"Starting Corba Setup Service";
        SMrunready.lock();
        FILE_LOG(logDEBUG) <<"SM sior is ready"<<endl;
        SMrunready.unlock();
        FILE_LOG(logDEBUG)<<"SM sior:"<<_siorRun;
        smSetup_i* mySMsetupService= new smSetup_i(_siorRun);
        PortableServer::ObjectId_var mySMsetupService_oid=mycorba::instance().getPOA()->activate_object(mySMsetupService);
        CORBA::Object_var setupService_obj=mySMsetupService->_this();
        CORBA::String_var sior ( mycorba::instance().getORB()->object_to_string ( setupService_obj.in() ) );
        FILE_LOG ( logINFO ) <<"setupService_obj:'"<< ( char* ) sior<<"'";
        PortableServer::POAManager_var pmgr= mycorba::instance().getPOA()->the_POAManager();
        pmgr->activate();
        FILE_LOG ( logINFO ) <<"Starting Searching AS Service";

        CORBA::Object_var nsobj =mycorba::instance().getORB()->resolve_initial_references("NameService");

        CosNaming::NamingContext_var initnc =CosNaming::NamingContext::_narrow(nsobj);
        try {

            CosNaming::Name name;

            name.length(2);
            name[0].id = CORBA::string_dup("AddressServer");
            name[0].kind = CORBA::string_dup("");
            name[1].id = CORBA::string_dup("SM");
            name[1].kind = CORBA::string_dup("");

            FILE_LOG(logDEBUG)<<"Resolving sm object"<<endl;

// Name aufloesen
            CORBA::Object_var obj = initnc->resolve(name);


            FILE_LOG(logDEBUG) <<"Found sm object"<<endl;

// Objektzeiger in Accountzeiger umwandeln
            AddressServer::sm_var SM = AddressServer::sm::_narrow(obj);

            FILE_LOG(logDEBUG) <<"Found addressserver cast successfull"<<endl;


            ::CORBA::ULong id=SM->registerSM(sior);


            mycorba::instance().run();
        }
        catch (CosNaming::NamingContext::NotFound & nf)
        {
            FILE_LOG(logERROR)<<"Caught CosNaming::notFoundException."<<endl;
        }
        catch (CosNaming::NamingContext::CannotProceed & nf)
        {
            FILE_LOG(logERROR)<<"Caught CosNaming::CannotProceed."<<endl;
        }
        catch (CosNaming::NamingContext::InvalidName & nf)
        {
            FILE_LOG(logERROR)<<"Caught CosNaming::InvalidName."<<endl;
        }
        catch (CosNaming::NamingContext::AlreadyBound & nf)
        {
            FILE_LOG(logERROR)<<"Caught CosNaming::AlreadyBound."<<endl;
        }
        catch (CosNaming::NamingContext::NotEmpty & nf)
        {
            FILE_LOG(logERROR)<<"Caught CosNaming::NotEmpty."<<endl;
        }


        mycorba::instance().run();
    }
    catch ( CORBA::SystemException& )
    {
        FILE_LOG ( logERROR ) <<"Caught CORBA::SystemException.";
    }
    catch ( CORBA::Exception& )
    {
        FILE_LOG ( logERROR ) <<"Caught CORBA::Exception.";
    }
    catch ( omniORB::fatalException& fe )
    {
        FILE_LOG ( logERROR ) <<"Caught omniORB::fatalException:";
        FILE_LOG ( logERROR ) <<" file: "<<fe.file();
        FILE_LOG ( logERROR ) <<" line: "<<fe.line();
        FILE_LOG ( logERROR ) <<" mesg: "<<fe.errmsg();
    }
    catch ( ... )
    {
        FILE_LOG ( logERROR ) <<"Caught unknown exception.";
    }
}

//Sets up the SMrun Interface for the Communication during the Simulation
void listenSMrun ()
{
    SMrunready.lock();
    FILE_LOG ( logINFO ) <<"Starting Corba Setup Service";
    try
    {
        smRun_i* mySMRunService = new smRun_i();

        PortableServer::ObjectId_var mySMRunService_oid =mycorba::instance().getPOA()->activate_object ( mySMRunService );

        CORBA::Object_var SA_obj=mySMRunService->_this();

        CORBA::String_var sior ( mycorba::instance().getORB()->object_to_string ( SA_obj.in() ) );

        FILE_LOG ( logINFO ) <<"'"<< ( char* ) sior<<"'";

        _siorRun=(char*)sior;
        SMrunready.unlock();
        PortableServer::POAManager_var pmgr= mycorba::instance().getPOA()->the_POAManager();
        pmgr->activate();


    }
    catch ( CORBA::SystemException& )
    {
        FILE_LOG ( logERROR ) <<"Caught CORBA::SystemException.";
    }
    catch ( CORBA::Exception& )
    {
        FILE_LOG ( logERROR ) <<"Caught CORBA::Exception.";
    }
    catch ( omniORB::fatalException& fe )
    {
        FILE_LOG ( logERROR ) <<"Caught omniORB::fatalException:";
        FILE_LOG ( logERROR ) <<" file: "<<fe.file();
        FILE_LOG ( logERROR ) <<" line: "<<fe.line();
        FILE_LOG ( logERROR ) <<" mesg: "<<fe.errmsg();
    }
    catch ( ... )
    {
        FILE_LOG ( logERROR ) <<"Caught unknown exception.";
    }

}

int main ( int argc, char** args )

{


    po::options_description desc ( "Allowed options" );
    desc.add_options()
    ( "help,h", "produce help message" )
    ( "debug,d", po::value< unsigned int >(), "set the debug level [0-3]. Default 0" )
    ;
    po::variables_map vm;
    po::store ( po::parse_command_line ( argc, args, desc ), vm );
    po::notify ( vm );
    if ( vm.count ( "help" ) )
    {
        cout << desc << "\n";
        exit ( 0 );
    }
    if ( vm.count ( "debug" ) )
    {
        debuglevel=vm["debug"].as<unsigned int>();
        if ( debuglevel>3 ) debuglevel=3;
        cout <<"Setting DebugLevel to: "<<debuglevel<<endl;
        switch ( debuglevel )
        {
        case 	0:
            FILELog::ReportingLevel() = FILELog::FromString ( "ERROR"  	);
            break;
        case 	1:
            FILELog::ReportingLevel() = FILELog::FromString ( "WARNING" 	);
            break;
        case 	2:
            FILELog::ReportingLevel() = FILELog::FromString ( "INFO" 	);
            break;
        case 	3:
            FILELog::ReportingLevel() = FILELog::FromString ( "DEBUG" 	);
            break;
        default  :
            FILELog::ReportingLevel() = FILELog::FromString ( "ERROR" 	);
            break;
        }

    }
    else
    {
        cout <<"Using default DebugLevel: 0 "<<endl;
        FILELog::ReportingLevel() = FILELog::FromString ( "ERROR" );
    }


    //Register Adapter, LP, Sched, Me Factories

    TaxiStandRollbackableAdapterFactory* tsrbadf=new TaxiStandRollbackableAdapterFactory();

    TaxiStandSqtwAdapterFactory* tssqtwadf=new TaxiStandSqtwAdapterFactory();

    TaxiStandMeFactory* tsmef=new TaxiStandMeFactory();
    CustomerSourceMeFactory* csmef=new CustomerSourceMeFactory();


    RollbackableLpFactory* rbf=new RollbackableLpFactory();
    SourceLpFactory* sourcelpf=new SourceLpFactory();

    LvtBasedSchedFactory* lvtsf=new LvtBasedSchedFactory();
    RandomSchedFactory* rasf=new RandomSchedFactory();
    RoundRobinSchedFactory* rrsf=new RoundRobinSchedFactory();


    Factories::instance().addSchedCreator(lvtsf,"sm.LvtBased");
    Factories::instance().addSchedCreator(rasf,"sm.Random");
    Factories::instance().addSchedCreator(rrsf,"sm.RoundRobin");

    Factories::instance().addMeCreator(tsmef,"TaxiStand");
    Factories::instance().addMeCreator(csmef,"CustomerSource");

    Factories::instance().addLpCreator(sourcelpf,"SourceLp");
    Factories::instance().addLpCreator(rbf,"RollbackableLp");

    Factories::instance().addAdCreator(tsrbadf,"TaxistandRollbackableAdapter");
    Factories::instance().addAdCreator(tssqtwadf,"TaxiStandSqtwAdapter");




    //init corba
    mycorba::instance().init(argc,args);


    //Start the Run CorbaInterface
    boost::thread* listenthread=new boost::thread ( &listenSMrun);
    FILE_LOG(logDEBUG)<<"Created listen thread!!!";

    //Start the Setup Corba Interface
    boost::thread* setupthread=new boost::thread (&listenSMsetup);






    //wait for the listen and setup thread to finish
    listenthread->join();

    FILE_LOG(logDEBUG)<<"finished listen thread!!!";
    setupthread->join();
    FILE_LOG(logDEBUG)<<"finished setup thread!!!";

// 	CClientSM corbaAS;
// 	unsigned int id=corbaAS.callregisterSM ( "test" );
// 	unsigned int id2=corbaAS.callregisterSM ( "test2" );
// 	unsigned int id3=corbaAS.callregisterSM ( "test3" );
// 	unsigned int id4=corbaAS.callregisterSM ( "test4" );
// 	FILE_LOG ( logINFO ) <<"main: sm id:"<<id<<endl;
// 	corbaAS.callunregisterSM ( id );

    /*


    vector<unsigned int> me_v;
    me_v.push_back(2);
    SourceLp* testlp;


    CorbaMessageHandler* cmh=new CorbaMessageHandler();



    ModelEntity* me_test=fac.createMe("TaxiStand",testlp,me_v);
    fac.delMe(me_test,"TaxiStand");

    me_test=fac.createMe("CustomerSource",testlp,me_v);


    SubmodelManager* sm=new SubmodelManager(1,cmh,&fac);

    sm->initSched("RoundRobin",5000.0);
    cout <<"Initsched done"<<endl;
    sm->initLp(1,me_v,"TaxiStand","RollbackableLp","TaxistandSqtwAdapter");
    me_v.clear();
    me_v.push_back(1);
    sm->initLp(2,me_v,"CustomerSource","SourceLp","SourceAdapter");
    sm->initLp(3,me_v,"CustomerSource","SourceLp","SourceAdapter");
    cout <<"here#1"<<endl;
    Message* test=new TaxiArrival(5.4,1,true);
    Message* test2=new CustomerArrival(27.0,1,true);
    cout <<"here#2"<<endl;
    cmh->receiveMessage(test);
    cmh->receiveMessage(test2);
    test2=new CustomerArrival(29.0,1,true);
    cmh->receiveMessage(test2);
    test2=new CustomerArrival(2.0,1,true);
    cmh->receiveMessage(test2);

    cout <<"initLp done."<<endl;
    sm->run();
    cout<<"Finished run"<<endl;*/

};
